﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Atha.Contracts;
using Atha.Extensions.Strings;
using Atha.Extensions.TestRunTestResults;
using Microsoft.Practices.ServiceLocation;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;

namespace Atha.Services.Host
{
	public class AthaTestService : IAthaTestService
	{
		public TestScriptContract[] OpenTests()
		{
			var reader = new TestScriptDirectoryReader(@"C:\__code\CodePlex\atha\debug\", true);

			var responses = new List<TestScriptContract>(0);

			reader.GetScripts(testScripts =>
			{
				responses = new List<TestScriptContract>(testScripts.Length);

				foreach (var testScript in testScripts)
				{
					var response = MapScriptResponse(testScript);

					responses.Add(response);
				}
			});

			return responses.ToArray();
		}

		public TestScriptContract OpenTest(string path)
		{
			var reader = new TestScriptFileReader(path);

			var response = new TestScriptContract();

			reader.GetScript(script => response = MapScriptResponse(script));

			return response;
		}

		public TestResult[] Execute(ExecuteTestScriptRequest testScript)
		{
			try
			{
				var unity = new UnityContainer();
				var serviceLocator = new UnityServiceLocator(unity);

				unity.LoadConfiguration();

				ServiceLocator.SetLocatorProvider(() => serviceLocator);

				var testRunContext = unity.Resolve<ITestRunContext<AggregateTestResult>>(
					testScript.TestLanguage,
					new ParameterOverride("name", testScript.TestLanguage),
					new ParameterOverride("script", testScript.TestScript));

				var testResults = testRunContext.Execute();

				return testResults.Results.Select(result => BuildResults(result)).ToArray();
			}
			catch (ResolutionFailedException resolutionFailedException)
			{
				Trace.TraceError(resolutionFailedException.Message);
				return new[] { new TestResult { Name = "Unsupported test language \"{0}\".".FormatWith(testScript.TestLanguage), Status = false } };
			}
			catch (Exception exception)
			{
				Trace.TraceError(exception.Message);
				Debug.Fail(exception.Message);
				return new[] { new TestResult { Name = "Test execution failed.", Status = false } };
			}
		}

		public void SaveTest(TestScriptContract testScript)
		{
			var writer = new TestScriptFileWriter();

			writer.SaveScript(new BasicTestScript(testScript.Language, testScript.Name, testScript.Content));
		}

		private static TestScriptContract MapScriptResponse(ITestScript testScript)
		{
			var response = new TestScriptContract
			{
				Name = testScript.Title,
				Language = testScript.Language,
				Content = testScript.Content
			};

			return response;
		}

		private static TestResult BuildResults(ITestRunTestResult testRunTestResult)
		{
			var childResults = new List<TestResult>();

			if (testRunTestResult is AggregateTestResult)
			{
				foreach (var childTestResult in (testRunTestResult as AggregateTestResult).Results)
				{
					childResults.Add(BuildResults(childTestResult));
				}
			}

			var result = new TestResult { Name = testRunTestResult.Name, Results = childResults.ToArray(), Status = testRunTestResult.Succeeded() ? true : testRunTestResult.Failed() ? false : (bool?)null };

			return result;
		}
	}
}
